package org.jmc.refmanager.web.wicket.form;

import java.util.Date;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.apache.wicket.PageParameters;
import org.apache.wicket.datetime.markup.html.basic.DateLabel;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.jmc.refmanager.domain.model.Change;
import org.jmc.refmanager.domain.model.Change.Operation;
import org.jmc.refmanager.domain.model.Change.TaskStatus;
import org.jmc.refmanager.domain.model.OrgUnit;
import org.jmc.refmanager.domain.model.OrgUnit.Complement;
import org.jmc.refmanager.domain.model.User;
import org.jmc.refmanager.domain.service.ChangeManager;
import org.jmc.refmanager.domain.service.OrgUnitManager;
import org.jmc.refmanager.domain.service.UserManager;
import org.jmc.refmanager.domain.service.exception.BusinessException;
import org.jmc.refmanager.security.SecurityHelper;
import org.jmc.refmanager.util.Update;
import org.jmc.refmanager.util.UpdatesList;
import org.jmc.refmanager.util.UpdatesList.UpdateType;
import org.jmc.refmanager.util.Util;
import org.jmc.refmanager.web.wicket.model.ChangeModel;
import org.jmc.refmanager.web.wicket.model.EnumPropertyModel;
import org.jmc.refmanager.web.wicket.page.panel.ChangePanel;
import org.jmc.refmanager.web.wicket.page.validation.MyGroupTasksListPage;
import org.jmc.refmanager.web.wicket.page.validation.TaskEditMoveOrDeletePage;
import org.jmc.refmanager.web.wicket.page.validation.TaskEditNewChangePage;
import org.jmc.refmanager.web.wicket.page.validation.TaskEditPage;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;

public class TaskViewForm extends Form<Change> {
	private static final long serialVersionUID = 1L;

	@SpringBean(name="orgUnitManager")
	private OrgUnitManager orgUnitManager;

	@SpringBean(name="taskManager")
	private ChangeManager taskManager;

	@SpringBean(name="userManager")
	private UserManager userManager;

	public TaskViewForm(String id, ChangeModel model) {
		super(id, model);
		setModel(new CompoundPropertyModel<Change>(model));
		
		setOutputMarkupId(false);
		
		final String matricule = SecurityHelper.getAuthentication().getName();
		final Change change = getModelObject();
		final Integer taskId = change.getId();
		boolean owner = false;
		
		if (change.getStatus() == TaskStatus.RESERVED) {
			owner = matricule.equalsIgnoreCase(change.getLockedByUser().getMatricule());
		}
		
		fillChangeInfo(change);
		
		switch (change.getOperation()) {
		case CREATE:
			fillCreateForm(change);
			break;

		case UPDATE:
		case MOVE_UPDATE:
		case DELETE:
			fillUpdateOrDeleteForm(change);
			break;

		default:
			throw new RuntimeException("Impossible de visualiser la tâche. Non implémenté");
		}
		
		Button btnLock = new Button("btnLock") {
			private static final long serialVersionUID = 1L;

			/* (non-Javadoc)
			 * @see org.apache.wicket.markup.html.form.Button#onSubmit()
			 */
			@Override
			public void onSubmit() {
				PageParameters params = new PageParameters();
				params.add("id", String.valueOf(taskId));
				
				try {
					// Appel e la couche de service
					User approver = userManager.getByMatricule(matricule);
					taskManager.checkout(change, approver);
					
					getSession().info("La tâche a été réservée avec succès.");
					setResponsePage(MyGroupTasksListPage.class, params);
				} catch (ConstraintViolationException e) {
					error("La tâche n'a pas été pu être réservée.");
					Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
					for (ConstraintViolation<?> constraintViolation : violations) {
						error(constraintViolation.getMessage());
					}
					setResponsePage(MyGroupTasksListPage.class, params);
				} catch (BusinessException be) {
					error(be.getMessage());
					setResponsePage(MyGroupTasksListPage.class, params);
				}
			}
		};
		btnLock.setVisible(change.getStatus() == TaskStatus.AVAILABLE);
		add(btnLock);

		Button btnUnlock = new Button("btnUnlock") {
			private static final long serialVersionUID = 1L;

			/* (non-Javadoc)
			 * @see org.apache.wicket.markup.html.form.Button#onSubmit()
			 */
			@Override
			public void onSubmit() {
				PageParameters params = new PageParameters();
				params.add("id", String.valueOf(taskId));

				try {
					// Appel e la couche de service
					User approver = userManager.getByMatricule(matricule);
					taskManager.revert(change, approver);
					
					getSession().info("La tâche a été libérée avec succès.");
					setResponsePage(MyGroupTasksListPage.class, params);
				} catch (ConstraintViolationException e) {
					error("La tâche n'a pas été pu être libérée.");
					Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
					for (ConstraintViolation<?> constraintViolation : violations) {
						error(constraintViolation.getMessage());
					}
					setResponsePage(MyGroupTasksListPage.class, params);
				} catch (BusinessException be) {
					error(be.getMessage());
					setResponsePage(MyGroupTasksListPage.class, params);
				}
			}
		};
		btnUnlock.setVisible(owner);
		add(btnUnlock);

		Button btnValidate = new Button("btnValidate") {
			private static final long serialVersionUID = 1L;

			/* (non-Javadoc)
			 * @see org.apache.wicket.markup.html.form.Button#onSubmit()
			 */
			@Override
			public void onSubmit() {
				PageParameters params = new PageParameters();
				params.add("id", String.valueOf(taskId));

				switch (change.getOperation()) {
				case CREATE:
					setResponsePage(TaskEditNewChangePage.class, params);
					break;

				case MOVE:
				case DELETE:
					setResponsePage(TaskEditMoveOrDeletePage.class, params);
					break;
					
				default:
					setResponsePage(TaskEditPage.class, params);
					break;
				}
			}
		};
		btnValidate.setVisible(owner);
		add(btnValidate);

		Button btnCancel = new Button("btnCancel") {
			private static final long serialVersionUID = 1L;
			@Override
			public void onSubmit() {
				setResponsePage(MyGroupTasksListPage.class);
			}
		};
		// Pas de validation
		btnCancel.setDefaultFormProcessing(false);
		add(btnCancel);
	}

	/**
	 * 
	 * @param change
	 */
	private void fillChangeInfo(Change change) {
		add(new Label("operation", 
				new EnumPropertyModel<Operation>(change, "operation", this, Operation.class)));
		add(new Label("status", 
				new EnumPropertyModel<TaskStatus>(change, "status", this, TaskStatus.class)));
		add(new Label("authorName", new PropertyModel<String>(change, "authorName")));
		add(DateLabel.forDatePattern("creationDate", 
				new PropertyModel<Date>(change, "creationDate"), "dd/MM/yyyy"));

		String lockedByUserMatricule;
		if (change.getStatus() == TaskStatus.RESERVED) {
			User lockedByUser = change.getLockedByUser();
			lockedByUserMatricule = lockedByUser.getMatricule();
			add(DateLabel.forDatePattern("modificationDateTime",
					new PropertyModel<Date>(change, "modificationDateTime"), "dd/MM/yyyy HH:mm"));
		} else {
			lockedByUserMatricule = "n/a";
			add(new Label("modificationDateTime", "n/a"));
		}

		add(new Label("lockedByUser", lockedByUserMatricule));
	}
	
	/**
	 * 
	 * @param change
	 */
	private void fillCreateForm(Change change) {
		// Recupere l'objet contenant les changements
		String xmlChanges = change.getObject();

		Serializer serializer = new Persister();
		OrgUnit newService = null;
		try {
			newService = serializer.read(OrgUnit.class, xmlChanges);
		} catch (Exception e) {
			throw new IllegalStateException(
					"Impossible de desérialiser les changements", e);
		}

		// Recheche du service parent e partir de son identifiant
		// Cet identifiant ne change pas meme s'il a été deplace.
		Integer parentId = newService.getParentId();
		OrgUnit parent = orgUnitManager.findById(parentId);
		newService.setParent(parent);

		add(new Label("codeUO", new PropertyModel<String>(newService, "codeUO")));
		add(new Label("type", new PropertyModel<String>(newService, "type")));
		add(new Label("parentType", new PropertyModel<String>(newService, "parent.type")));

		String saaOrSao;
		if (newService.isSaa()) {
			saaOrSao = "Service administratif";
		} else if (newService.isSao()) {
			saaOrSao = "Service opérationnel";
		} else {
			saaOrSao = "n/a";
		}
		
		add(new Label("saaOrSao", saaOrSao));
		add(new Label("libCourt", new PropertyModel<String>(newService, "libCourt")));
		add(new Label("libLong", new PropertyModel<String>(newService, "libLong")));
		add(new Label("libComplet", new PropertyModel<String>(newService, "libComplet")));
		add(new Label("parentLibCourt", new PropertyModel<String>(newService, "parent.libCourt")));
		add(new Label("numRue", new PropertyModel<String>(newService, "numRue")));
		add(new Label("complementNumRue", 
				new EnumPropertyModel<Complement>(newService, "complementNumRue", this, Complement.class)));

		add(new Label("adresse", new PropertyModel<String>(newService, "adresse")));
		add(new Label("complementAdresse", new PropertyModel<String>(newService, "complementAdresse")));
		add(new Label("codePostal", new PropertyModel<String>(newService, "codePostal")));
		add(new Label("commune", new PropertyModel<String>(newService, "commune")));
		add(new Label("mail", new PropertyModel<String>(newService, "mail")));
		add(new Label("telephone", new PropertyModel<String>(newService, "telephone")));
		add(new Label("rescom", new PropertyModel<String>(newService, "rescom")));

		add(new Label("adressePostale", new PropertyModel<String>(newService, "adressePostale")));
		
		add(DateLabel.forDatePattern("dateFin", 
				new PropertyModel<Date>(newService, "dateFin"), "dd/MM/yyyy"));
		add(DateLabel.forDatePattern("dateEffet", 
				new PropertyModel<Date>(newService, "dateEffet1"), "dd/MM/yyyy"));

		TextArea<String> motif = new TextArea<String>("motif", new PropertyModel<String>(newService, "motifCreation"));
		motif.setEnabled(false);
		add(motif);
	}

	/**
	 * 
	 * @param change
	 */
	private void fillUpdateOrDeleteForm(Change change) {
		// Recupere l'objet contenant les changements
		String xmlChanges = change.getObject();

		Serializer serializer = new Persister();
		UpdatesList updates = null;
		try {
			updates = serializer.read(UpdatesList.class, xmlChanges);
		} catch (Exception e) {
			throw new IllegalStateException(
					"Impossible de desérialiser les changements", e);
		}

		// Recupere le service en consultation
		OrgUnit viewService = orgUnitManager.findById(updates.getServiceId());
		
		// Applique les mises e jour
		viewService.applySerializedChanges(xmlChanges);
		
		Update moveUpdate = updates.getUpdateOf(UpdateType.PARENT);
		if (moveUpdate != null && !moveUpdate.isEmpty()) {
			Integer parentId = (Integer) moveUpdate.getAttributeUpdates().get("parentId");
			OrgUnit parent = orgUnitManager.findById(parentId);
//			add(new Label("parentLibCourt", new PropertyModel<String>(parent, "libCourt")));
			add(new ChangePanel("parentLibCourt", new PropertyModel<String>(parent, "libCourt"),
					viewService.getParent().getLibCourt()));

		} else {
			add(new Label("parentLibCourt", new PropertyModel<String>(viewService, "parent.libCourt")));
		}
		add(new Label("parentType", new PropertyModel<String>(viewService, "parent.type")));
		
		add(new Label("codeUO", new PropertyModel<String>(viewService, "codeUO")));
		String saaOrSao;
		if (viewService.isSaa()) {
			saaOrSao = "Service administratif";
		} else if (viewService.isSao()) {
			saaOrSao = "Service opérationnel";
		} else {
			saaOrSao = "n/a";
		}
		add(new Label("saaOrSao", saaOrSao));
		add(new Label("type", new PropertyModel<String>(viewService, "type")));

		//		add(new Label("libCourt", new PropertyModel<String>(viewService, "libCourt")));
		add(new ChangePanel("libCourt", new PropertyModel<String>(viewService, "libCourt"),
				viewService.getBeforeValue("libCourt")));

		//		add(new Label("libLong", new PropertyModel<String>(viewService, "libLong")));
		add(new ChangePanel("libLong", new PropertyModel<String>(viewService, "libLong"),
				viewService.getBeforeValue("libLong")));

		//		add(new Label("libComplet", new PropertyModel<String>(viewService, "libComplet")));
		add(new ChangePanel("libComplet", new PropertyModel<String>(viewService, "libComplet"),
				viewService.getBeforeValue("libComplet")));

		//		add(new Label("adressePostale", new PropertyModel<String>(viewService, "adressePostale")));
		add(new ChangePanel("adressePostale", new PropertyModel<String>(viewService, "adressePostale"),
				viewService.getBeforeValue("adressePostale")));

		//		add(new Label("numRue", new PropertyModel<String>(viewService, "numRue")));
		add(new ChangePanel("numRue", new PropertyModel<String>(viewService, "numRue"),
				viewService.getBeforeValue("numRue")));
		
		//		add(new Label("complementNumRue", new EnumPropertyModel<Complement>(viewService, "complementNumRue", this, Complement.class)));
		Object complementNumRue = viewService.getBeforeValue("complementNumRue");
		String complementNumRueValue = Util.getComplementLabel(this, complementNumRue);
//		if (complementNumRue != null) {
//			Complement complement = Complement.valueOf(complementNumRue.toString());
//			String key = EnumMessageKeyProvider.getMessageKey(complement, Complement.class);
//			complementNumRueValue = this.getString(key);
//		}
		add(new ChangePanel("complementNumRue", 
				new EnumPropertyModel<Complement>(viewService, "complementNumRue", this, Complement.class),
				complementNumRueValue));

//		add(new Label("adresse", new PropertyModel<String>(viewService, "adresse")));
		add(new ChangePanel("adresse", new PropertyModel<String>(viewService, "adresse"),
				viewService.getBeforeValue("adresse")));

//		add(new Label("complementAdresse", new PropertyModel<String>(viewService, "complementAdresse")));
		add(new ChangePanel("complementAdresse", new PropertyModel<String>(viewService, "complementAdresse"),
				viewService.getBeforeValue("complementAdresse")));
		
//		add(new Label("codePostal", new PropertyModel<String>(viewService, "codePostal")));
		add(new ChangePanel("codePostal", new PropertyModel<String>(viewService, "codePostal"),
				viewService.getBeforeValue("codePostal")));

//		add(new Label("commune", new PropertyModel<String>(viewService, "commune")));
		add(new ChangePanel("commune", new PropertyModel<String>(viewService, "commune"),
				viewService.getBeforeValue("commune")));

//		add(new Label("mail", new PropertyModel<String>(viewService, "mail")));
		add(new ChangePanel("mail", new PropertyModel<String>(viewService, "mail"),
				viewService.getBeforeValue("mail")));
		
//		add(new Label("telephone", new PropertyModel<String>(viewService, "telephone")));
		add(new ChangePanel("telephone", new PropertyModel<String>(viewService, "telephone"),
				viewService.getBeforeValue("telephone")));

		//		add(new Label("rescom", new PropertyModel<String>(viewService, "rescom")));
		add(new ChangePanel("rescom", new PropertyModel<String>(viewService, "rescom"), 
				viewService.getBeforeValue("rescom")));

		add(DateLabel.forDatePattern("dateFin", 
				new PropertyModel<Date>(viewService, "dateFin"), "dd/MM/yyyy"));
		add(DateLabel.forDatePattern("dateEffet", 
				new PropertyModel<Date>(viewService, "dateEffet1"), "dd/MM/yyyy"));

		TextArea<String> motif = null;
		if (change.getOperation() == Operation.DELETE) {
			motif = new TextArea<String>("motif", new PropertyModel<String>(viewService, "motifSuppression"));
		} else {
			motif = new TextArea<String>("motif", new Model<String>("n/a"));
			motif.setVisible(false);
		}
		motif.setEnabled(false);
		add(motif);

	}
}
